
//创建请求类，通过类的方式进行封装，会让代码有复用性
//框架思维：除了内部默认，也要提供外部可以修改内部默认值
class WXRequest {
    //定义默认拦截器对象
    //包含请求和响应拦截器
    interceptors = {
        //默认请求拦截器，如果外部重写就会覆盖默认的
        //在发送请求之前，对请求参数进行修改和新增
        request: (config) => {
            console.log("默认拦截器，请求前");
            //对config进行修改
            return config
        },
        //默认响应拦截器 
        //对响应的数据进行逻辑处理
        response: (response) => {
            console.log("默认拦截器，响应后");
            //进行逻辑处理
            return response
        }
    }
    //定义数组队列，解决多个请求loadding加载框的问题
    //基本逻辑：请求时往队列里面增加数据，请求结束后减少数据，当长度为0时销毁加载框
    //初始值时个空数组，用来存储请求标识
    queue = []


    //实例属性，默认请求参数
    //这里只是默认值，如果修改默认值可以在实例化”new WXRequest“时候，或者调用request(options)时进入覆盖
    defaults = {
        baseURL: '',//'https://news-at.zhihu.com',//ip地址
        url: "",//接口请求的路径
        data: null,//请求参数
        method: "GET",//默认请求方式
        //默认请求头
        header: {
            'Content-Type': 'application/json'//数据交互格式
        },
        timeout: 6000,//默认超时时长1分钟
        isLoading: true//是否显示加载框
    }


    //创建和初始化类的属性和方法
    //params实例时的参数，可以重写默认参数
    constructor(params = {}) {
        //合并请求参数
        //对传递的参数，默认参数进行合并成一个新对象”{}“
        //把新对象"{}"赋值给defaults
        //传递的参数需要覆盖默认的参数，所以要把传递的参数放到最后
        this.defaults = Object.assign({}, this.defaults, params)
    }
    //options对象和wx.request传递的参数保持一致
    request(options) {

        //调用请求拦截起，目的是修改请求参数
        //修改options请求参数后，在返回给请求继续执行
        options = this.interceptors.request(options)
        //合并请求地址
        options.url = this.defaults.baseURL + options.url
        //合并请求参数，把options覆盖defaults
        options = { ...this.defaults, ...options }
        console.log('请求参数 options=', options);
        if (options.isLoading) {
            //解决laodding闪烁问题
            //如果有新的请求就清除上一个请求
            if (this.timerId) {
                clearTimeout(this.timerId)
            }
            //queue如果是null就显示loadding
            if (this.queue.length == 0) {
                //显示laoding
                wx.showLoading({
                    title: '正在请求',
                })
            }
            //每次请求往队列中增加一个标识
            this.queue.push("request")
            console.log("this.queue.length1=", this.queue.length);
        }

        //使用Promise封装wxwx.request
        //resolve成功回调函数，在.then接收
        //reject失败回调函数，在.catch中接收
        return new Promise((resolve, reject) => {
            //http请求
            wx.request({
                //"..."展开运算符，把options里面的属性赋值给wx.request相应的属性
                //在传值的时候要和wx.request保持一致
                ...options,
                //接口请求成功
                //注意：只要服务端有状态码响应就会走，比如当404的时候可能会走成功函数
                success: (res) => {//箭头函数
                    console.log('http请求成功=', res);
                    //把返回的数据处理好在调用resolve回调到外部
                    //把响应的和请求参数合并到一个空对象中，方便调试
                    //不管请求失败或成功都传给了响应拦截器，增加一个属性isSuccess，true说明执行了success，false说明执行了fail
                    const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
                    //传给拦截器，让interceptors.response内部的逻辑处理后返回给resolve让外部回调
                    const resObj = this.interceptors.response(mergeRes)
                    //回调给外部
                    resolve(resObj)
                },
                //请求失败
                //网络异常走：网络超时会走fail
                fail: (err) => {
                    console.log('http请求失败=', err);
                    //失败里面也需要拦截器
                    const mergeRes = Object.assign({}, err, { config: options, isSuccess: false })
                    const resObj = this.interceptors.response(mergeRes)
                    reject(resObj)
                },
                complete: () => {//失败，成功回调
                    if (options.isLoading) {
                        //每次请求后，删除一个标识
                        // this.queue.pop()
                        // console.log("this.queue.length2=", this.queue.length);
                        //解决多个请求始终弹一个loadding
                        //判断队列是否还有,如果空销毁loadding弹出框
                        // if (this.queue.length == 0) {
                        //     wx.hideLoading()
                        // }

                        //下面要解决laodding闪烁问题
                        /*
                        一个请求里面还有请求，如何保持只有一个laodding？
                        解释下面代码逻辑：
                        1、当外层请求进入request时this.timerId为null,laodding队列增加一条‘request’
                        2、外层请求走到了complete后进行this.queue.pop()，这个时候队列为0，如果立马销毁laodding框
                        那么内层请求加载框就会立马弹出来，这样就成了两个加载框出现闪烁问题，无法保证始终一个加载框，
                        为了保持始终一个加载框,必须往队列里面添加一条那么代码就是  this.queue.length == 0&&this.queue.push('a')，
                        这个时候队列里还有一个”a“, 所以加载框不消失
                        3、当内请求开始的时候就会清除上一个定时器clearTimeout(this.timerId)，这个时候队列在增加一个‘request’，这个时候
                        还剩下a和request， 当内请求完毕后进入complete时this.queue.pop()清除”a“这个时候还剩下request",
                        当进入定时器后在pop,消息队列为0，那么最后销毁加载框和定时器
                        */
                        this.queue.pop()
                        if (this.queue.length == 0) {
                            this.queue.push('a')
                        }

                        this.timerId = setTimeout(() => {
                            this.queue.pop()
                            if (this.queue.length == 0) {
                                wx.hideLoading()
                            }
                            //关闭这个定时器
                            clearTimeout(this.timerId)
                        }, 1)
                    }


                }
            })
        })
    }
    //封装get
    get(url, data = {}, config = {}) {
        //合并参数,config会和前面的{}合并，有重复的会覆盖掉
        var options = Object.assign({ url, data, method: "GET" }, config)
        //返回请求方法
        return this.request(options)
    }
    //封装post
    post(url, data = {}, config = {}) {
        //合并参数,config会覆盖前面相同的参数
        var options = Object.assign({ url, data, method: "POST" }, config)
        //返回请求方法
        return this.request(options)
    }
    //并发请求，同时请求
    //正常使用： await Promise.all([请求1，请求2])
    all(...promise) {
        return Promise.all(promise)
    }
}

//---------------示例化代码，方便测试，以后会提取成多个文件-------------------
var baseurl = "https://news-at.zhihu.com"
//这里传参的意义：可以重写默认参数，提供外部修改
const instance = new WXRequest({
    baseURL: baseurl,//"https://news-at.zhihu.com",
    timeout: 15000,
    isLoading: true
})
//重写默认拦截器，重写后默认的不会在走
//外部配置请求拦截器，会覆盖默认的
//config是用户传递的请求参数，在这个里面进行修改后在返回给请求，继续请求
instance.interceptors.request = (config) => {
    console.log("拦截器-请求前，处理逻辑=", config);
    console.log("拦截器-请求前，处理逻辑=", config.header);
    //在请求发送之前做逻辑处理
    //添加token
    if( undefined==config.header){
        config.header={}
    }
    config.header.token = "asdfsdfsdfsdfsadfasfasddf"
    return config
}
//重写默认拦截器，重写后默认的不会在走
//外部配置响应拦截器，会覆盖默认的
//把response根据自己的逻辑处理好后在返回给resolve
instance.interceptors.response = (response) => {
    //在响应之后做逻辑处理
    //{isSuccess}=response,等价于var isSuccess=response.isSuccess
    const { isSuccess } = response
    //走的是fail给出提示
    if (!isSuccess) {//走的fail函数
        wx.showToast({
            title: '网略异常',
            icon: 'error'
        })
    }

    console.log("拦截器-响应后，处理逻辑 响应参数=", response);
    switch (response.statusCode) {
        case 200:
            response.msg = "请求成功"
            response.code = 0
            break
        case 208://或者直接跳转到登录页面
            response.msg = "请重新登录，token失效"
            response.code = 208
            break
        default:
            response.msg = "网络异常，网络超时"
            response.code = 1
            break
    }
    return response
}

//暴露到外部，方便其他文件使用
export default instance